﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thanhlichtek.BOS.Bullhorn;
using Thanhlichtek.BOS.Ongage;
using Thanhlichtek.BOS.Bullhorn.BullhornApi;
using System.Collections;
using System.IO;
using System.Xml;
using System.Threading;

namespace Thanhlichtek.BOS.Sync
{
    public class SyncManager
    {
        //new
        public IList BullhornAccounts;
        public Ongage.Account OngageAccount;
        public List<BullhornAccountSyncInfo> BullhornAccountSyncInfos { get; set; }
        public GetListsResult.ListInfo[] OngageLists;
        public string MasterListId = "";
        public ContactLookupResult[] MasterListContacts;
        public List<ContactLookupResult> MasterListContactsToCheckForDeleting;
        private OngageService _ongageService;

        private const String _bullhornAccountsFile = "Profile\\BullhornAccounts.xml";
        private const String _ongageAccountFile = "Profile\\OngageAccount.xml";
        private const int maxTry = 10;
        //end new

        public Bullhorn.Account bullhornAccountItem;
        public List<distributionListDto> BullhornLists;
        public distributionListDto BullhornList;
        public GetListsResult.ListInfo OngageList;
        public clientContactDto ClientContact;



        public event EventHandler ConnectingStart;
        public event EventHandler ConnectingStop;
        public event EventHandler GettingBullhornListsStart;
        public event EventHandler GettingBullhornListsStop; 
        public event EventHandler GettingOngageListsStart;
        public event EventHandler GettingOngageListsStop; 
        public event EventHandler GettingBullhornListInfoStart;
        public event EventHandler GettingBullhornListInfoStop;
        public event EventHandler SyncListStart;
        public event EventHandler SyncListStop;
        public event EventHandler SyncStart;
        public event EventHandler SyncStop;
        public event EventHandler GetClientContactIdsStart;
        public event EventHandler GetClientContactIdsStop;
        public event EventHandler CreateContactSuccess;
        public event EventHandler UpdateContactSuccess;
        public event EventHandler SyncContactFail;
        public event EventHandler SyncContactIgnore;
        public event EventHandler SyncContactDeletedState;
        public event EventHandler SyncContactsStart;
        public event EventHandler SyncContactsStop;
        public event EventHandler SyncError;


        public SyncManager()
        {
            //Get Ongage account
            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(Ongage.Account));
            if (File.Exists(_ongageAccountFile))
            {
                XmlReader xr = XmlReader.Create(_ongageAccountFile);
                this.OngageAccount = (Ongage.Account)x.Deserialize(xr);
                this.OngageAccount.Password = CryptoHelper.Decrypt(this.OngageAccount.Password);
                xr.Close();
            }

            //Get Bullhorn accounts
            System.Xml.Serialization.XmlSerializer x2 = new System.Xml.Serialization.XmlSerializer(typeof(List<Bullhorn.Account>));
            if (File.Exists(_bullhornAccountsFile))
            {
                XmlReader xr = XmlReader.Create(_bullhornAccountsFile);
                this.BullhornAccounts = (IList)x2.Deserialize(xr);
                xr.Close();
                foreach (var bullhornAccountObj in this.BullhornAccounts)
                {
                    ((Bullhorn.Account)bullhornAccountObj).Password = CryptoHelper.Decrypt(((Bullhorn.Account)bullhornAccountObj).Password);
                    ((Bullhorn.Account)bullhornAccountObj).ApiKey = CryptoHelper.Decrypt(((Bullhorn.Account)bullhornAccountObj).ApiKey);
                }
            }

            _ongageService = new Ongage.OngageService(OngageAccount.UserName, OngageAccount.Password, OngageAccount.AccountCode);

        }

        public void GetBullhornListInfoDict(BullhornService bullhornService, BullhornAccountSyncInfo bullhornAccountSyncInfo)
        {
            if (GettingBullhornListsStart != null)
            {
                this.GettingBullhornListsStart(this, EventArgs.Empty);
            }
            var listIds = bullhornService.GetListIds();
            var count = 0;
            foreach (var listIdObj in listIds)
            {
                count++;
                //hai test: limit the number of lists
                //if (count > 2) break;
                int listId = Int32.Parse(listIdObj.ToString());
                if (GettingBullhornListInfoStart != null)
                {
                    this.GettingBullhornListInfoStart(this, new SyncEventArgs(String.Format("Getting Bullhorn lists ({0}/{1})", count, listIds.Count())));
                }
                var distributionList = bullhornService.GetListInfo(listId);
                var bullhornListInfo = new BullhornListInfo(bullhornAccountSyncInfo, distributionList);
                bullhornListInfo.CorporateUser = bullhornService.GetCorporateUser(distributionList.ownerID);
                bullhornAccountSyncInfo.BullhornListInfoDict.Add(listId, bullhornListInfo);
            }

            if (GettingBullhornListsStop != null)
            {
                this.GettingBullhornListsStop(this, new SyncEventArgs() { BullhornAccountSyncInfoObj = bullhornAccountSyncInfo});
            }
            
        }

        SyncListThread syncListThread;

        public void StopSyncList()
        {
            try
            {
                syncListThread.StopAllThreads();

            }
            catch (Exception)
            {
                
                //throw;
            }
        }

        public void StartSync()
        {

            var message = "Idle";
            try
            {
                if (SyncStart != null)
                {
                    this.SyncStart(this, new SyncEventArgs("Start synchronizing"));
                }

                if (ConnectingStart != null)
                {
                    this.ConnectingStart(this, new SyncEventArgs("Connecting to Ongage ..."));
                }

                //get Ongage lists
                this.OngageLists = this.GetOngageLists();

                MasterListId = this.OngageLists.FirstOrDefault(x => x.name.Equals("MasterList", StringComparison.OrdinalIgnoreCase)) == null ?
                    "" : this.OngageLists.FirstOrDefault(x => x.name.Equals("MasterList", StringComparison.OrdinalIgnoreCase)).id;

                if (String.IsNullOrEmpty(MasterListId))
                {
                    throw new Exception("Please create MasterList in Ongage to start synchronization.");
                }
                MasterListContactsToCheckForDeleting = _ongageService.GetContactsFromList(MasterListId).ToList();
                foreach (var bullhornAccountObj in BullhornAccounts)
                {
                    message = "Idle";
                    bullhornAccountItem = (Bullhorn.Account)bullhornAccountObj;
                    BullhornAccountSyncInfo bullhornAccountSyncInfo = new BullhornAccountSyncInfo(bullhornAccountItem);

                    if (ConnectingStart != null)
                    {
                        this.ConnectingStart(this, new SyncEventArgs("Connecting to Bullhorn ..."));
                    }

                    Bullhorn.BullhornService bullhornService = new BullhornService(bullhornAccountItem.UserName,
                        bullhornAccountItem.Password, bullhornAccountItem.ApiKey, true);
                    if (ConnectingStop != null)
                    {
                        this.ConnectingStop(this, new SyncEventArgs("Connected"));
                    }

                    //get Bullhorn lists
                    this.GetBullhornListInfoDict(bullhornService, bullhornAccountSyncInfo);

                    var count = 0;
                    foreach (var bullhornListInfoItem in bullhornAccountSyncInfo.BullhornListInfoDict.Values)
                    {
                        count++;
                        //hai test
                        //if (count > 2)
                        //{
                        //    break;
                        //}
                        syncListThread = new SyncListThread() { SyncManagerObj = this, _BullhornListInfo = bullhornListInfoItem};
                        syncListThread.SyncList();
                    }
                }
                //Delete from MasterList
                try
                {
                    foreach (var ongageContactItem in MasterListContactsToCheckForDeleting)
                    {
                        _ongageService.DeleteContact(ongageContactItem.id);
                        //var searchContactResult = bullhornService.SearchClientContactByEmail(ongageContactItem.email);
                        //if (!searchContactResult.Any())
                        //{
                        //    //delete from MasterList
                        //    _ongageService.DeleteContact(ongageContactItem.id);
                        //}
                    }

                }
                catch (Exception ex)
                {
                }
            }
            catch (Exception ex)
            {

                if (SyncError != null)
                {
                    this.SyncError(this, new SyncEventArgs(ex.Message));
                }
            }
            finally
            {
                if (SyncStop != null)
                {
                    this.SyncStop(this, new SyncEventArgs(message));
                }
            }
        }

        class SyncListThread
        {
            private BullhornService _threadBullhornService;
            private OngageService _threadOngageService;
            public BullhornListInfo _BullhornListInfo { get; set; }
            public SyncManager SyncManagerObj { get; set; }

            private Thread[] accountThreads = new Thread[20];

            public void StopAllThreads()
            {
                foreach (var accountThread in accountThreads)
                {
                    try
                    {
                        accountThread.Abort();
                    }
                    catch (Exception)
                    {
                    }
                }
            }

            public void SyncList()
            {
                _threadBullhornService = new BullhornService(_BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.UserName, 
                    _BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.Password,
                    _BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.ApiKey, false);
                _threadOngageService = new OngageService(SyncManagerObj.OngageAccount.UserName, SyncManagerObj.OngageAccount.Password, SyncManagerObj.OngageAccount.AccountCode);

                distributionListDto bullhornList = _BullhornListInfo.DistributionList;

                //Search Ongage list with the same name
                String listId = "";
                var foundList = SyncManagerObj.OngageLists.FirstOrDefault(x => x.name.Trim().Equals(bullhornList.name.Trim(), StringComparison.OrdinalIgnoreCase));
                //Ongage list not found, do not sync
                if (foundList == null)
                {
                    //foundList = SyncManagerObj.OngageLists.FirstOrDefault(x => x.name.Equals("MasterList"));

                    if (SyncManagerObj.SyncListStop != null)
                    {
                        SyncManagerObj.SyncListStop(SyncManagerObj, new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                    }
                    return;
                }
                _BullhornListInfo.OngageList = foundList;
                if (SyncManagerObj.SyncListStart != null)
                {

                    SyncManagerObj.SyncListStart(SyncManagerObj, new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                }
                //if (foundList == null)
                //{
                //    if (SyncManagerObj.SyncListStop != null)
                //    {
                //        SyncManagerObj.SyncListStop(SyncManagerObj, new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                //    }
                //    return;
                //}

                listId = foundList.id;

                if (SyncManagerObj.GetClientContactIdsStart != null)
                {
                    SyncManagerObj.GetClientContactIdsStart(SyncManagerObj, new SyncEventArgs(String.Format("Getting number of client contacts of {0} list ...", bullhornList.name)));
                }

                //get Bullhorn client contact ids
                var clientContactIds = _threadBullhornService.GetClientContactIds(bullhornList);

                if (clientContactIds != null)
                {
                    var count = 0;
                    _BullhornListInfo.ClientContactInfoDict = new Dictionary<int, ClientContactInfo>();
                    foreach (var clientContactId in clientContactIds)
                    {
                        count++;
                        //test: limit the number of client contact
                        //if (count > 50) break;

                        _BullhornListInfo.ClientContactInfoDict.Add(Int32.Parse(clientContactId.ToString()), 
                            new ClientContactInfo() { BullhornListInfoObj = _BullhornListInfo, ClientContact = null, State = SyncState.NotSynced, Message = "Pending", EmailState = EmailFixedState.Valid });
                    }

                }
                if (SyncManagerObj.GetClientContactIdsStop != null)
                {
                    SyncManagerObj.GetClientContactIdsStop(SyncManagerObj, new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                }


                if (SyncManagerObj.SyncContactsStart != null)
                {
                    SyncManagerObj.SyncContactsStart(SyncManagerObj, new SyncEventArgs("Synchronizing contacts of " + bullhornList.name));
                }

                //Get accounts of Ongage list
                _BullhornListInfo.OngageContacts = _threadOngageService.GetContactsFromList(listId);
                //Get accounts of the MasterList
                SyncManagerObj.MasterListContacts = _threadOngageService.GetContactsFromList(SyncManagerObj.MasterListId);

                for (int i = 0; i < accountThreads.Length; i++)
                {
                    SyncAccountThread syncAccountThread = new SyncAccountThread();
                    syncAccountThread._BullhornListInfo = _BullhornListInfo;
                    syncAccountThread.SyncManagerObj = SyncManagerObj;
                    accountThreads[i] = new Thread(new ThreadStart(syncAccountThread.SyncAccount));
                    accountThreads[i].IsBackground = true;
                    accountThreads[i].Start();
                }

                foreach (var accountThread in accountThreads)
                {
                    accountThread.Join();
                }

                //Delete from Ongage
                if (!listId.Equals(SyncManagerObj.MasterListId))
                {
                    try
                    {
                        var ongageContacts = _BullhornListInfo.OngageContacts;
                        foreach (var ongageContactItem in ongageContacts)
                        {
                            var query = from x in _BullhornListInfo.ClientContactInfoDict.Values
                                        where x.ClientContact != null && 
                                            x.ClientContact.email.Equals(ongageContactItem.email, StringComparison.OrdinalIgnoreCase) && 
                                            !x.ClientContact.isDeleted
                                        select x;
                            if (!query.Any())
                            {
                                //delete from Ongage
                                _threadOngageService.DeleteContact(ongageContactItem.id);
                                _BullhornListInfo.NotExistInBullhorn.Add(ongageContactItem);
                                ////delete from MasterList too
                                //SyncManagerObj.MasterListContactsToCheckForDeleting.RemoveAll(x => x.email.Equals(ongageContactItem.email, StringComparison.OrdinalIgnoreCase));
                                //var masterListContactToDelete = SyncManagerObj.MasterListContacts.FirstOrDefault(x => x.email.Equals(ongageContactItem.email));
                                //if (masterListContactToDelete != null)
                                //{
                                //    _threadOngageService.DeleteContact(masterListContactToDelete.id);
                                //}
                            }
                        }

                    }
                    catch (Exception ex)
                    {
                        var message = ex.Message;
                    }
                }

                if (SyncManagerObj.SyncListStop != null)
                {

                    SyncManagerObj.SyncListStop(SyncManagerObj, new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                }
            }
        }

        class SyncAccountThread
        {
            private BullhornService _threadBullhornService;
            private OngageService _threadOngageService;
            public BullhornListInfo _BullhornListInfo { get; set; }
            public SyncManager SyncManagerObj { get; set; }
            private static Object _lockObj = new Object();

            /// <summary>
            /// found (same email same bullhornId): update
            /// found (same email, differenct bullhornId): update
            /// found (different email, same bullhornId): create new Ongage with Active status, delete old Ongage account
            /// not found (same email, same bullhornId): create new
            /// </summary>
            public void SyncAccount()
            {
                _threadBullhornService = new BullhornService(_BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.UserName,
                    _BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.Password,
                    _BullhornListInfo.BullhornAccountSyncInfoObj.BullhornAccount.ApiKey, false);
                _threadOngageService = new OngageService(SyncManagerObj.OngageAccount.UserName, SyncManagerObj.OngageAccount.Password, SyncManagerObj.OngageAccount.AccountCode);

                while (true)
                {
                    KeyValuePair<int, ClientContactInfo> clientContactInfoDictItem;
                    ClientContactInfo clientContactInfo;
                    int clientContactId;
                    lock (_lockObj)
                    {
                        var queryClientContactInfo = from x in _BullhornListInfo.ClientContactInfoDict
                                                     where x.Value.State == SyncState.NotSynced
                                                     select x;
                        clientContactInfoDictItem = queryClientContactInfo.ToList().FirstOrDefault();

                        if (clientContactInfoDictItem.Value == null)
                        {
                            return;
                        }

                        clientContactId = clientContactInfoDictItem.Key;
                        clientContactInfo = clientContactInfoDictItem.Value;
                        clientContactInfo.State = SyncState.Syncing;
                    }

                    //sync the client contact
                    var tryCount = 0;

                    clientContactDto clientContact;
                    while (true)
                    {
                        tryCount++;
                        try
                        {
                            clientContact = _threadBullhornService.GetClientContact(clientContactId);
                            clientContactInfo.ClientContact = clientContact;
                            //company of a contact
                            clientContactInfo.ClientCorporation = _threadBullhornService.GetClientCorporation(clientContact.clientCorporationID);
                            //owner of a contact
                            clientContactInfo.Owner = _threadBullhornService.GetCorporateUser(clientContact.ownerID);

                            if (clientContact.isDeleted)
                            {
                                //do not sync
                                clientContactInfo.State = SyncState.DeletedState;
                                clientContactInfo.Message = "DeletedState";
                                if (SyncManagerObj.SyncContactDeletedState != null)
                                {
                                    SyncManagerObj.SyncContactDeletedState(SyncManagerObj,
                                        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                }
                                break; //the corresponding Ongage account will be deleted
                            }

                            if (clientContact == null)
                            {
                                clientContactInfo.State = SyncState.Failed;
                                clientContactInfo.Message = "Failed: Cannot find the client contact with id " + clientContactId;
                                if (SyncManagerObj.SyncContactFail != null)
                                {
                                    SyncManagerObj.SyncContactFail(SyncManagerObj, 
                                        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                }
                                break;

                            }

                            if (String.IsNullOrEmpty(clientContact.email))
                            {
                                //do not sync
                                clientContactInfo.State = SyncState.Ignored;
                                clientContactInfo.Message = "Ignored";
                                if (SyncManagerObj.SyncContactIgnore != null)
                                {
                                    SyncManagerObj.SyncContactIgnore(SyncManagerObj, 
                                        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                }
                                break;
                            }
                            else
                            {
                                //fix email
                                var fixedEmail = Utils.FixEmailAddress(clientContact.email);
                                if (!clientContact.email.Equals(fixedEmail))
                                {
                                    clientContact.email = fixedEmail;
                                    clientContactDto result = _threadBullhornService.SaveClientContact(clientContact);
                                    clientContactInfo.EmailState = EmailFixedState.Fixed;
                                }
                                //if (clientContact.email.Contains(" "))
                                //{
                                //    clientContact.email = clientContact.email.Replace(" ", "");
                                //    clientContactDto result = _threadBullhornService.SaveClientContact(clientContact);
                                //    clientContactInfo.EmailState = EmailFixedState.Fixed;

                                //}

                                //remove this contact from MasterListContactsToCheckForDeleting
                                lock (SyncManagerObj.MasterListContactsToCheckForDeleting)
                                {
                                    SyncManagerObj.MasterListContactsToCheckForDeleting.RemoveAll(
                                        x => x.email.Equals(clientContact.email, StringComparison.OrdinalIgnoreCase));
                                }

                                //Check if the email is duplicated
                                if (tryCount == 1)
                                {
                                    lock (_BullhornListInfo.DistinctEmails)
                                    {
                                        if (_BullhornListInfo.DistinctEmails.Contains(clientContact.email))
                                        {
                                            clientContactInfo.State = SyncState.Failed;
                                            clientContactInfo.Message = "Failed: Duplicated contact.";
                                            //clientContactInfo.EmailState = EmailFixedState.NotFixed;
                                            if (SyncManagerObj.SyncContactFail != null)
                                            {
                                                SyncManagerObj.SyncContactFail(SyncManagerObj,
                                                    new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                            }
                                            break;
                                        }
                                        else
                                        {
                                            _BullhornListInfo.DistinctEmails.Add(clientContact.email);
                                        }
                                    }
                                }
                            }
                            String listId = _BullhornListInfo.OngageList.id;

                            //Sync with MasterList
                            if (!listId.Equals(SyncManagerObj.MasterListId)) //this condition is always true
                            {
                                //search contact in Ongage
                                Ongage.ContactLookupResult foundOngageContactMaster = SyncManagerObj.MasterListContacts.FirstOrDefault(
                                    x => x.email.Equals(clientContact.email, StringComparison.OrdinalIgnoreCase));

                                var ongageContactMaster = new Ongage.Contact();
                                ongageContactMaster.email = clientContact.email;
                                ongageContactMaster.list_id = SyncManagerObj.MasterListId;
                                ongageContactMaster.fields = new Ongage.Contact.Fields();
                                ongageContactMaster.fields.email = clientContact.email;
                                ongageContactMaster.fields.company = clientContactInfo.ClientCorporation.name;
                                ongageContactMaster.fields.title = clientContact.occupation;
                                ongageContactMaster.fields.first_name = clientContact.firstName;
                                ongageContactMaster.fields.last_name = clientContact.lastName;
                                ongageContactMaster.fields.state = clientContact.address.state;
                                ongageContactMaster.fields.zipcode = clientContact.address.zip;
                                ongageContactMaster.fields.owner = clientContactInfo.Owner.name;
                                ongageContactMaster.fields.bullhorn_id = clientContactId.ToString();
                                //found same email
                                if (foundOngageContactMaster != null)
                                {
                                    //Update Bullhorn -> Ongage
                                    var result = _threadOngageService.UpdateContact(ongageContactMaster);

                                    if (result.metadata.error.Equals("false", StringComparison.OrdinalIgnoreCase))
                                    {
                                        //Do nothing
                                    }
                                    else
                                    {
                                        //retry
                                        throw new Exception("Failed: " + 
                                            (String.IsNullOrEmpty(result.GetPayloadMessage()) ?
                                            result.GetMetadataMessage() : result.GetPayloadMessage()));
                                        //clientContactInfo.State = SyncState.Failed;
                                        //clientContactInfo.Message = "Failed: " + 
                                        //    (String.IsNullOrEmpty(result.GetPayloadMessage()) ?
                                        //    result.GetMetadataMessage() : result.GetPayloadMessage());
                                        //if (SyncManagerObj.SyncContactFail != null)
                                        //{
                                        //    SyncManagerObj.SyncContactFail(SyncManagerObj,
                                        //        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        //}
                                        //break;
                                    }

                                    //Update Ongage -> Bullhorn
                                    if (clientContact.massMailOptOut != foundOngageContactMaster.IsOptOut())
                                    {
                                        clientContact.massMailOptOut = foundOngageContactMaster.IsOptOut();
                                        clientContactDto resultOptOut = _threadBullhornService.SaveClientContact(clientContact);
                                    }
                                }
                                else
                                {

                                    //create new Ongage contact
                                    var resultCreate = _threadOngageService.AddContact(ongageContactMaster);
                                    if (resultCreate.metadata.error.Equals("false", StringComparison.OrdinalIgnoreCase))
                                    {
                                        //search contact in Ongage
                                        Ongage.ContactLookupResult foundOngageContactMaster2 = SyncManagerObj.MasterListContacts.FirstOrDefault(
                                            x => x.bullhorn_id.Equals(clientContactId.ToString(), StringComparison.OrdinalIgnoreCase));
                                        if (foundOngageContactMaster2 != null)
                                        {
                                            //found (different email, same bullhornId): create new Ongage with Active status, delete old Ongage account, set Bullhorn to opt in
                                            _threadOngageService.SetContactStatus(
                                                ongageContactMaster.email, SyncManagerObj.MasterListId, ContactStatusAction.resubscribe);
                                            _threadOngageService.DeleteContact(foundOngageContactMaster2.id);

                                            clientContact.massMailOptOut = false;
                                            clientContactDto resultOptOut = _threadBullhornService.SaveClientContact(clientContact);
                                        }
                                        else
                                        {
                                            if (clientContact.massMailOptOut)
                                            {
                                                //the Ongage status is unsubcribe when it was created ??? So no need to do this command ???
                                                _threadOngageService.SetContactStatus(
                                                    ongageContactMaster.email, SyncManagerObj.MasterListId, ContactStatusAction.unsubscribe);
                                            }
                                            
                                        }
                                    }
                                    else
                                    {
                                        //if failed to create
                                        if (resultCreate.payload.message.ToLower().Contains("please insert a valid email address"))
                                        {
                                            lock (_BullhornListInfo.DistinctEmails)
                                            {
                                                _BullhornListInfo.DistinctEmails.Remove(clientContact.email);
                                            }
                                            clientContactInfo.State = SyncState.Failed;
                                            clientContactInfo.Message = "Failed: " + resultCreate.payload.message;
                                            clientContactInfo.EmailState = EmailFixedState.NotFixed;
                                            if (SyncManagerObj.SyncContactFail != null)
                                            {
                                                SyncManagerObj.SyncContactFail(SyncManagerObj,
                                                    new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                            }
                                            break;
                                        }

                                        if (resultCreate.payload.message.ToLower().Contains("email already exists"))
                                        {
                                            //clientContactInfo.State = SyncState.Failed;
                                            //clientContactInfo.Message = "Failed: Duplicated contact.";
                                            ////clientContactInfo.EmailState = EmailFixedState.NotFixed;
                                            //if (SyncManagerObj.SyncContactFail != null)
                                            //{
                                            //    SyncManagerObj.SyncContactFail(SyncManagerObj,
                                            //        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                            //}
                                            //break;
                                        }
                                        else
                                        {
                                            //retry
                                            throw new Exception("Failed: " + resultCreate.payload.message);
                                            //clientContactInfo.State = SyncState.Failed;
                                            //clientContactInfo.Message = "Failed: " + resultCreate.payload.message;
                                            //if (SyncManagerObj.SyncContactFail != null)
                                            //{
                                            //    SyncManagerObj.SyncContactFail(SyncManagerObj,
                                            //        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                            //}
                                            //break;
                                        }

                                    }
                                }
                            }
                            //End of Sync with MasterList

                            //search contact in Ongage
                            Ongage.ContactLookupResult foundOngageContact = _BullhornListInfo.OngageContacts.FirstOrDefault(
                                x => x.email.Equals(clientContact.email, StringComparison.OrdinalIgnoreCase));

                            var ongageContact = new Ongage.Contact();
                            ongageContact.email = clientContact.email;
                            ongageContact.list_id = listId;
                            ongageContact.fields = new Ongage.Contact.Fields();
                            ongageContact.fields.email = clientContact.email;
                            ongageContact.fields.company = clientContactInfo.ClientCorporation.name;
                            ongageContact.fields.title = clientContact.occupation;
                            ongageContact.fields.first_name = clientContact.firstName;
                            ongageContact.fields.last_name = clientContact.lastName;
                            ongageContact.fields.state = clientContact.address.state;
                            ongageContact.fields.zipcode = clientContact.address.zip;
                            ongageContact.fields.owner = clientContactInfo.Owner.name;
                            ongageContact.fields.bullhorn_id = clientContactId.ToString();

                            if (foundOngageContact != null)
                            {
                                //found
                                //Update Bullhorn -> Ongage
                                if (foundOngageContact.first_name.Equals(clientContact.firstName)
                                    && foundOngageContact.last_name.Equals(clientContact.lastName) && false)
                                {
                                    clientContactInfo.State = SyncState.Updated;
                                    clientContactInfo.Message = "Updated";
                                    if (SyncManagerObj.UpdateContactSuccess != null)
                                    {
                                        SyncManagerObj.UpdateContactSuccess(SyncManagerObj, 
                                            new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                    }

                                }
                                else
                                {
                                    //var result = _threadOngageService.UpdateContact(clientContact.email, clientContact.firstName, clientContact.lastName, listId);
                                    var result = _threadOngageService.UpdateContact(ongageContact);

                                    if (result.metadata.error.Equals("false", StringComparison.OrdinalIgnoreCase))
                                    {
                                        clientContactInfo.State = SyncState.Updated;
                                        clientContactInfo.Message = "Updated";
                                        if (SyncManagerObj.UpdateContactSuccess != null)
                                        {
                                            SyncManagerObj.UpdateContactSuccess(SyncManagerObj, 
                                                new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        }
                                    }
                                    else
                                    {
                                        //retry
                                        throw  new Exception("Failed: " + 
                                            (String.IsNullOrEmpty(result.GetPayloadMessage()) ? 
                                            result.GetMetadataMessage() : result.GetPayloadMessage()));
                                        //clientContactInfo.State = SyncState.Failed;
                                        //clientContactInfo.Message = "Failed: " + 
                                        //    (String.IsNullOrEmpty(result.GetPayloadMessage()) ? 
                                        //    result.GetMetadataMessage() : result.GetPayloadMessage());
                                        //if (SyncManagerObj.SyncContactFail != null)
                                        //{
                                        //    SyncManagerObj.SyncContactFail(SyncManagerObj, 
                                        //        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        //}
                                        //break;
                                    }

                                }

                                if (listId.Equals(SyncManagerObj.MasterListId))
                                {
                                    //Update Ongage -> Bullhorn
                                    if (clientContact.massMailOptOut != foundOngageContact.IsOptOut())
                                    {
                                        clientContact.massMailOptOut = foundOngageContact.IsOptOut();
                                        clientContactDto resultOptOut = _threadBullhornService.SaveClientContact(clientContact);
                                        clientContactInfo.State = SyncState.Updated;
                                        clientContactInfo.Message = "Updated";
                                        if (SyncManagerObj.UpdateContactSuccess != null)
                                        {
                                            SyncManagerObj.UpdateContactSuccess(SyncManagerObj,
                                                new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        }
                                    }
                                }
                                else
                                {
                                    //Update Bullhorn -> Ongage (MasterList -> list)
                                    if (clientContact.massMailOptOut != foundOngageContact.IsOptOut())
                                    {
                                        if (clientContact.massMailOptOut)
                                        {
                                            _threadOngageService.SetContactStatus(
                                                foundOngageContact.email, listId, ContactStatusAction.unsubscribe);
                                        }
                                        else
                                        {
                                            _threadOngageService.SetContactStatus(
                                                foundOngageContact.email, listId, ContactStatusAction.resubscribe);
                                        }
                                        clientContactInfo.State = SyncState.Updated;
                                        clientContactInfo.Message = "Updated";
                                        if (SyncManagerObj.UpdateContactSuccess != null)
                                        {
                                            SyncManagerObj.UpdateContactSuccess(SyncManagerObj,
                                                new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        }
                                    }
                                }
                            }
                            else
                            {
                                //not found
                                //create

                                //var resultCreate = _threadOngageService.AddContact(clientContact.email, clientContact.firstName, clientContact.lastName, listId);
                                var resultCreate = _threadOngageService.AddContact(ongageContact);

                                if (resultCreate.metadata.error.Equals("false", StringComparison.OrdinalIgnoreCase))
                                {
                                    Ongage.ContactLookupResult foundOngageContact2 = _BullhornListInfo.OngageContacts.FirstOrDefault(
                                        x => x.bullhorn_id.Equals(clientContactId.ToString(), StringComparison.OrdinalIgnoreCase));

                                    if (foundOngageContact2 != null)
                                    {
                                        //found (different email, same bullhornId): create new Ongage with Active status, delete old Ongage account
                                        _threadOngageService.SetContactStatus(
                                            clientContact.email, listId, ContactStatusAction.resubscribe);
                                        _threadOngageService.DeleteContact(foundOngageContact2.id);
                                    }
                                    if (clientContact.massMailOptOut)
                                    {
                                        _threadOngageService.SetContactStatus(
                                            clientContact.email, listId, ContactStatusAction.unsubscribe);
                                    }
                                    clientContactInfo.State = SyncState.Created;
                                    clientContactInfo.Message = "Created";
                                    if (SyncManagerObj.CreateContactSuccess != null)
                                    {
                                        SyncManagerObj.CreateContactSuccess(SyncManagerObj,
                                            new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                    }
                                }
                                else
                                {
                                    //if failed to create
                                    if (resultCreate.payload.message.ToLower().Contains("please insert a valid email address"))
                                    {
                                        lock (_BullhornListInfo.DistinctEmails)
                                        {
                                            _BullhornListInfo.DistinctEmails.Remove(clientContact.email);
                                        }
                                        clientContactInfo.State = SyncState.Failed;
                                        clientContactInfo.Message = "Failed: " + resultCreate.payload.message;
                                        clientContactInfo.EmailState = EmailFixedState.NotFixed;
                                        if (SyncManagerObj.SyncContactFail != null)
                                        {
                                            SyncManagerObj.SyncContactFail(SyncManagerObj,
                                                new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        }
                                        break;
                                    }
                                    
                                    if (resultCreate.payload.message.ToLower().Contains("email already exists"))
                                    {
                                        if (clientContact.massMailOptOut)
                                        {
                                            _threadOngageService.SetContactStatus(clientContact.email, listId, ContactStatusAction.unsubscribe);
                                        }
                                        clientContactInfo.State = SyncState.Created;
                                        clientContactInfo.Message = "Created";
                                        if (SyncManagerObj.CreateContactSuccess != null)
                                        {
                                            SyncManagerObj.CreateContactSuccess(SyncManagerObj,
                                                new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        }
                                        //clientContactInfo.State = SyncState.Failed;
                                        //clientContactInfo.Message = "Failed: Duplicated contact.";
                                        ////clientContactInfo.EmailState = EmailFixedState.NotFixed;
                                        //if (SyncManagerObj.SyncContactFail != null)
                                        //{
                                        //    SyncManagerObj.SyncContactFail(SyncManagerObj,
                                        //        new SyncEventArgs() { BullhornListInfoObj = _BullhornListInfo });
                                        //}
                                        break;
                                    }
                                    else
                                    {

                                        throw new Exception("Failed: " + resultCreate.payload.message);

                                    }
                                    
                                }

                            }

                            break;
                        }
                        catch (Exception ex)
                        {
                            if (tryCount > maxTry)
                            {
                                clientContactInfo.State = SyncState.Failed;
                                clientContactInfo.Message = "Failed: " + ex.Message;
                                if (SyncManagerObj.SyncContactFail != null)
                                {
                                    SyncManagerObj.SyncContactFail(SyncManagerObj, new SyncEventArgs("Cannot synchronize: " + ex.StackTrace) { BullhornListInfoObj = _BullhornListInfo });
                                }
                                break;
                            }
                            Thread.Sleep(1000);
                        }

                    }

                    //todo: need to research why some contacts is still in Syncing state!!!!!!!
                    if (clientContactInfo.State == SyncState.Syncing)
                    {
                        clientContactInfo.State = SyncState.NotSynced;
                        clientContactInfo.Message = "Pending";
                        clientContactInfo.ClientContact = null;
                    }
                }

            }
        }

        private GetListsResult.ListInfo[] GetOngageLists()
        {
            if (GettingOngageListsStart != null)
            {
                this.GettingOngageListsStart(this, EventArgs.Empty);
            }
            GetListsResult.ListInfo[] listInfos = _ongageService.GetLists();
            if (GettingOngageListsStop != null)
            {
                this.GettingOngageListsStop(this, EventArgs.Empty);
            }
            return listInfos;
        }


    }
}
